package design.simdi.code.final_paper.core.services.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import design.simdi.code.final_paper.core.daos.BibliographyDao;
import design.simdi.code.final_paper.core.daos.DomainDao;
import design.simdi.code.final_paper.core.daos.IdeaDao;
import design.simdi.code.final_paper.core.daos.KeywordDao;
import design.simdi.code.final_paper.core.daos.MesajDao;
import design.simdi.code.final_paper.core.daos.PersonDao;
import design.simdi.code.final_paper.core.daos.ProjectDao;
import design.simdi.code.final_paper.core.daos.SesiuneDao;
import design.simdi.code.final_paper.core.daos.StudentAdmisDao;
import design.simdi.code.final_paper.core.daos.StudentCandidatDao;
import design.simdi.code.final_paper.core.entities.Bibliography;
import design.simdi.code.final_paper.core.entities.Domain;
import design.simdi.code.final_paper.core.entities.Idea;
import design.simdi.code.final_paper.core.entities.Keyword;
import design.simdi.code.final_paper.core.entities.Mesaj;
import design.simdi.code.final_paper.core.entities.Person;
import design.simdi.code.final_paper.core.entities.Project;
import design.simdi.code.final_paper.core.entities.Sesiune;
import design.simdi.code.final_paper.core.entities.StudentAdmis;
import design.simdi.code.final_paper.core.entities.StudentCandidat;
import design.simdi.code.final_paper.core.enums.MessageStatus;
import design.simdi.code.final_paper.core.services.ApplicationService;
import design.simdi.code.final_paper.utils.StatusStudentCandidat;

/**
 * 
 * @author Marius Dumitriu - (Jun 22, 2010 - 1:41:22 PM)
 * 
 */
@Transactional
@Service("applicationService")
public class ApplicationServiceImpl implements ApplicationService {

	@Autowired
	private KeywordDao keywordDao;
	@Autowired
	private BibliographyDao bibliographyDao;
	@Autowired
	private IdeaDao ideaDao;
	@Autowired
	private SesiuneDao sesiuneDao;

	@Autowired
	private DomainDao domainDao;
	@Autowired
	private StudentCandidatDao studentCandidatDao;
	@Autowired
	private ProjectDao projectDao;
	@Autowired
	private MesajDao mesajDao;
	@Autowired
	private PersonDao personDao;
	@Autowired
	private StudentAdmisDao studentAdmisDao;

	public void setStudentAdmisDao(StudentAdmisDao studentAdmisDao) {
		this.studentAdmisDao = studentAdmisDao;
	}

	public StudentAdmisDao getStudentAdmisDao() {
		return studentAdmisDao;
	}

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public PersonDao getPersonDao() {
		return personDao;
	}

	public void setMesajDao(MesajDao mesajDao) {
		this.mesajDao = mesajDao;
	}

	public MesajDao getMesajDao() {
		return mesajDao;
	}

	public void setProjectDao(ProjectDao projectDao) {
		this.projectDao = projectDao;
	}

	public ProjectDao getProjectDao() {
		return projectDao;
	}

	public void setStudentCandidatDao(StudentCandidatDao studentCandidatDao) {
		this.studentCandidatDao = studentCandidatDao;
	}

	public StudentCandidatDao getStudentCandidatDao() {
		return studentCandidatDao;
	}

	public void setDomainDao(DomainDao domainDao) {
		this.domainDao = domainDao;
	}

	public DomainDao getDomainDao() {
		return domainDao;
	}

	public void setSesiuneDao(SesiuneDao sesiuneDao) {
		this.sesiuneDao = sesiuneDao;
	}

	public SesiuneDao getSesiuneDao() {
		return sesiuneDao;
	}

	public void setIdeaDao(IdeaDao ideaDao) {
		this.ideaDao = ideaDao;
	}

	public IdeaDao getIdeaDao() {
		return ideaDao;
	}

	public void setBibliographyDao(BibliographyDao bibliographyDao) {
		this.bibliographyDao = bibliographyDao;
	}

	public BibliographyDao getBibliographyDao() {
		return bibliographyDao;
	}

	public void setKeywordDao(KeywordDao keywordDao) {
		this.keywordDao = keywordDao;
	}

	public KeywordDao getKeywordDao() {
		return keywordDao;
	}

	public void saveKeyword(Keyword keyword) {
		keywordDao.saveOrUpdate(keyword);
	}

	public void removeKeyword(Keyword keyword) {
		keywordDao.delete(keyword.getId());

	}

	public void removeBibliography(Bibliography bibliography) {
		bibliographyDao.delete(bibliography.getId());

	}

	public void saveBibliography(Bibliography bibliography) {
		// un pic de securitate
		String link = bibliography.getLink();
		link = link.trim();
		link = link.replaceAll(" ", "");
		bibliography.setLink(link);
		bibliographyDao.saveOrUpdate(bibliography);

	}

	public void removeIdea(Idea idea) {
		for (Keyword key : idea.getKeywords())
			keywordDao.delete(key.getId());
		ideaDao.delete(idea.getId());

	}

	public void saveIdea(Idea idea) {
		for (Keyword key : idea.getKeywords())
			if (key.getId() == null)
				// adaug pe cele noi
				keywordDao.saveOrUpdate(key);
			else
				// sterg cele vechi
				keywordDao.delete(key.getId());

		for (Bibliography b : idea.getBibliographies())
			if (b.getId() == null)
				// adaug pe cele noi
				bibliographyDao.saveOrUpdate(b);
			else
				// sterg cele vechi
				bibliographyDao.delete(b.getId());

		ideaDao.saveOrUpdate(idea);

	}

	public List<Keyword> findAllKeywordsByPerson(Person person) {

		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("person.id", person.getId()));

		return keywordDao.findAll(new Order[] {}, criterii);
	}

	public List<Bibliography> findAllBibliographiesByPerson(Person person) {
		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("person.id", person.getId()));

		return bibliographyDao.findAll(new Order[] {}, criterii);
	}

	public void removeSesiune(Sesiune sesiune) {
		sesiuneDao.delete(sesiune.getId());

	}

	public void saveSesiune(Sesiune sesiune) {
		sesiuneDao.saveOrUpdate(sesiune);
	}

	public void removeDomain(Domain domain) {
		for (Keyword key : domain.getKeywords())
			keywordDao.delete(key.getId());
		domainDao.delete(domain.getId());

	}

	public void saveDomain(Domain domain) {
		for (Keyword key : domain.getKeywords())
			if (key.getId() == null)
				// adaug pe cele noi
				keywordDao.saveOrUpdate(key);
			else
				// sterg cele vechi
				keywordDao.delete(key.getId());

		for (Bibliography b : domain.getBibliographies())
			if (b.getId() == null)
				// adaug pe cele noi
				bibliographyDao.saveOrUpdate(b);
			else
				// sterg cele vechi
				bibliographyDao.delete(b.getId());
		domainDao.saveOrUpdate(domain);
	}

	public void removeStudentCandidat(StudentCandidat studentCandidat) {
		studentCandidatDao.delete(studentCandidat.getId());

	}

	public void saveStudentCandidat(StudentCandidat studentCandidat) {
		studentCandidatDao.saveOrUpdate(studentCandidat);

	}

	public StudentCandidat findStudentCandidatByDomain(Domain domain, Person person) {
		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("domain.id", domain.getId()));
		if (person.getAbsolvent() != null)
			criterii.add(Restrictions.eq("absolvent.id", person.getAbsolvent().getId()));
		if (person.getStudent() != null)
			criterii.add(Restrictions.eq("student.id", person.getStudent().getId()));

		List<StudentCandidat> lista = studentCandidatDao.findAll(new Order[] {}, criterii);
		if (lista.size() == 0)
			return null;
		return lista.get(0);
	}

	public StudentCandidat findStudentCandidatByProject(Project project, Person person) {
		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("project.id", project.getId()));
		if (person.getAbsolvent() != null)
			criterii.add(Restrictions.eq("absolvent.id", person.getAbsolvent().getId()));
		if (person.getStudent() != null)
			criterii.add(Restrictions.eq("student.id", person.getStudent().getId()));

		List<StudentCandidat> lista = studentCandidatDao.findAll(new Order[] {}, criterii);
		if (lista.size() == 0)
			return null;
		return lista.get(0);
	}

	public StudentAdmis findStudentAdmisByProject(Project project, StudentCandidat sc) {
		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("project.id", project.getId()));
		if (sc.getAbsolvent() != null)
			criterii.add(Restrictions.eq("absolvent.id", sc.getAbsolvent().getId()));
		if (sc.getStudent() != null)
			criterii.add(Restrictions.eq("student.id", sc.getStudent().getId()));

		List<StudentAdmis> lista = studentAdmisDao.findAll(new Order[] {}, criterii);
		if (lista.size() == 0)
			return null;
		return lista.get(0);
	}

	public StudentAdmis findStudentAdmisByProject(Project project, Person person) {
		List<Criterion> criterii = new ArrayList<Criterion>();
		criterii.add(Restrictions.eq("project.id", project.getId()));
		if (person.getAbsolvent() != null)
			criterii.add(Restrictions.eq("absolvent.id", person.getAbsolvent().getId()));
		if (person.getStudent() != null)
			criterii.add(Restrictions.eq("student.id", person.getStudent().getId()));

		List<StudentAdmis> lista = studentAdmisDao.findAll(new Order[] {}, criterii);
		if (lista.size() == 0)
			return null;
		return lista.get(0);
	}

	private StudentAdmis findStudentAdmisByStudentCandidat(StudentCandidat sc) {
		List<Criterion> criterii = new ArrayList<Criterion>();
		if (sc.getAbsolvent() != null)
			criterii.add(Restrictions.eq("absolvent.id", sc.getAbsolvent().getId()));
		if (sc.getStudent() != null)
			criterii.add(Restrictions.eq("student.id", sc.getStudent().getId()));

		List<StudentAdmis> lista = studentAdmisDao.findAll(new Order[] {}, criterii);
		if (lista.size() == 0)
			return null;
		return lista.get(0);
	}

	public void removeProject(Project project) {
		for (Keyword key : project.getKeywords())
			keywordDao.delete(key.getId());
		projectDao.delete(project.getId());

	}

	public void saveProject(Project project) {
		for (Keyword key : project.getKeywords())
			if (key.getId() == null)
				// adaug pe cele noi
				keywordDao.saveOrUpdate(key);
			else
				// sterg cele vechi
				keywordDao.delete(key.getId());

		for (Bibliography b : project.getBibliographies())
			if (b.getId() == null)
				// adaug pe cele noi
				bibliographyDao.saveOrUpdate(b);
			else
				// sterg cele vechi
				bibliographyDao.delete(b.getId());
		projectDao.saveOrUpdate(project);

	}

	public void removeMesaj(Mesaj mesaj) {
		mesajDao.delete(mesaj.getId());

	}

	public void saveMesaj(Mesaj mesaj) {
		mesaj.setDate(new Date());
		mesajDao.saveOrUpdate(mesaj);
	}

	public void markMesajRead(Mesaj mesaj) {
		mesaj.setStatus(MessageStatus.READ);
		mesajDao.saveOrUpdate(mesaj);
	}

	public Person findPersoanaByStudentCandidat(StudentCandidat sc) {
		Person filter = new Person();
		if (sc.getStudent() != null)
			filter.setStudent(sc.getStudent());
		else
			filter.setAbsolvent(sc.getAbsolvent());

		List<Person> lista = personDao.findFiltered(filter);
		if (lista.size() >= 0)
			return lista.get(0);
		return null;
	}

	public Person findPersoanaByStudentAdmis(StudentAdmis sc) {
		Person filter = new Person();
		if (sc.getStudent() != null)
			filter.setStudent(sc.getStudent());
		else
			filter.setAbsolvent(sc.getAbsolvent());

		List<Person> lista = personDao.findFiltered(filter);
		if (lista.size() >= 0)
			return lista.get(0);
		return null;
	}

	public void changeStatus(StudentCandidat sc, StatusStudentCandidat status) {
		Domain d = sc.getDomain();
		Project p = sc.getProject();

		switch (status) {
		case ACCEPTAT:
			if (d != null) {
				d.setAvailableProjectsNo(d.getAvailableProjectsNo() - 1);
				domainDao.saveOrUpdate(d);
				d = domainDao.get(d.getId());
				// creez un proiect asociat domeniului
				Project proiectAsociat = new Project();
				proiectAsociat.setAuthor(d.getAuthor());
				proiectAsociat.setAvailable(false);

				proiectAsociat.setBibliographies(new HashSet<Bibliography>());
				for (Bibliography b : d.getBibliographies()) {
					proiectAsociat.getBibliographies().add(b.clone());
				}

				proiectAsociat.setCoordonator(d.getCoordonator());
				proiectAsociat.setDescription(d.getDescription());
				proiectAsociat.setDomeniu(d);

				proiectAsociat.setKeywords(new HashSet<Keyword>());
				for (Keyword k : d.getKeywords())
					proiectAsociat.getKeywords().add(k.clone());

				proiectAsociat.setName(d.getName());
				proiectAsociat.setNotes(d.getNotes());
				proiectAsociat.setSesiune(d.getSesiune());

				saveProject(proiectAsociat);

				proiectAsociat = projectDao.get(proiectAsociat.getId());

				// creez un student admis. sau absolvent
				StudentAdmis sa = new StudentAdmis();
				sa.setProject(proiectAsociat);
				sa.setAbsolvent(sc.getAbsolvent());
				sa.setStudent(sc.getStudent());

				studentAdmisDao.saveOrUpdate(sa);

			} else if (p != null) {
				p.setAvailable(false);
				projectDao.saveOrUpdate(p);
				p = projectDao.get(p.getId());

				// creez un student admis. sau absolvent
				StudentAdmis sa = new StudentAdmis();
				sa.setProject(p);
				sa.setAbsolvent(sc.getAbsolvent());
				sa.setStudent(sc.getStudent());

				studentAdmisDao.saveOrUpdate(sa);

			}
			break;

		case RESPINS:
			if (d != null) {
				d.setAvailableProjectsNo(d.getAvailableProjectsNo() + 1);
				domainDao.saveOrUpdate(d);

				StudentAdmis sa = findStudentAdmisByStudentCandidat(sc);
				if (sa != null) {
					studentAdmisDao.delete(sa.getId());

					Project proiectAsociat = sa.getProject();

					removeProject(proiectAsociat);
				}

			} else if (p != null) {
				p.setAvailable(true);
				projectDao.saveOrUpdate(p);
				StudentAdmis sa = findStudentAdmisByProject(p, sc);
				if (sa != null)
					studentAdmisDao.delete(sa.getId());

			}
			break;
		}
		sc.setStatus(status);
		studentCandidatDao.saveOrUpdate(sc);

	}

	public boolean canBeAdmited(StudentCandidat sc) {
		StudentCandidat filter = new StudentCandidat();
		if (sc.getStudent() != null)
			filter.setStudent(sc.getStudent());
		else
			filter.setAbsolvent(sc.getAbsolvent());

		List<StudentCandidat> lista = studentCandidatDao.findFiltered(filter);

		for (StudentCandidat stc : lista)
			if (stc.getStatus().equals(StatusStudentCandidat.ACCEPTAT))
				return false;
		return true;
	}

	public Person findPersoanaByProject(Project project) {
		// TODO Auto-generated method stub
		return null;
	}

	public void setDefault(Sesiune sesiune, boolean boole) {
		sesiune.setDefaulta(new Boolean(boole));
		sesiuneDao.saveOrUpdate(sesiune);

	}


}
